/*
 * Package il.ac.biu.cs.grossmm.api.cpp
 * File BaseCppConsumer.java
 * 
 * This is a part of presence service framework API. 
 * See javadoc for more information.
 *  
 * Copyright (C) 2006 Grossmann Mark
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.

 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 */
package il.ac.biu.cs.grossmm.api.cpp;

import il.ac.biu.cs.grossmm.api.keys.Key;
import il.ac.biu.cs.grossmm.api.keys.KeyPattern;
import il.ac.biu.cs.grossmm.api.presence.Entity;
import il.ac.biu.cs.grossmm.api.server.Component;
import il.ac.biu.cs.grossmm.api.server.ComponentManager;
import il.ac.biu.cs.grossmm.api.server.ServiceManager;
import il.ac.biu.cs.grossmm.api.server.Service;

/**
 * A base for implementation of <tt>CppServiceConsumer</tt> to simplify
 * developement of user objects.
 * 
 * @param <T>
 *            java object type to be used as a representation of a resource
 *            state
 * 
 * 
 * @see CppServiceConsumer
 */
public abstract class BaseCppConsumer<T> implements CppServiceConsumer<T>,
		Runnable, Component, Service {

	/**
	 * Name of this module
	 */
	protected final String consumerName;

	/**
	 * name of persistent provider to be used, null if a non-persistent provider
	 * should be used
	 */
	protected final String providerName;


	/**
	 * component manager for this module
	 */
	protected ComponentManager componentManager;

	/**
	 * service manager for this module
	 */
	protected ServiceManager serviceManager;

	/**
	 * the java class object for the T-parameter
	 */
	protected final Class<T> tClass;

	/**
	 * The key pattern for this module when it plays a role of a data processor
	 */
	protected final KeyPattern pattern;

	/**
	 * The service provider module for this consumer module
	 */
	protected CppServiceProvider provider;
	
	private boolean needThread;

	private Thread thread;

	private volatile ExecState state;

	private enum ExecState {
		STOPPED, STARTING, RUNNING, STOPPING
	}

	/**
	 * The constructor
	 * 
	 * @param consumerName
	 *            a symbolic identifier for this module
	 * @param tClass
	 *            the t-class of CPP consumer
	 * @param pattern
	 *            the key pattern of the CPP consumer
	 * @param providerName
	 *            name of persistent provider to be used, null if a
	 *            non-persistent provider should be used
	 * @param needThread
	 *            if true, a thread is automatically started when the service is
	 *            started
	 */
	public BaseCppConsumer(String consumerName, Class<T> tClass,
			KeyPattern pattern, String providerName, boolean needThread) {
		this.consumerName = consumerName;
		this.providerName = providerName;
		this.tClass = tClass;
		this.pattern = pattern;
		this.needThread = needThread;

		this.state = ExecState.STOPPED;
	}

	public void aborted(Object hint, Exception e) throws Exception {
		throw e;
	}

	public void terminated(Object serviceHint, Exception e) throws Exception {
		throw e;
	}

	public void setComponentManager(ComponentManager container)
			throws Exception {
		this.componentManager = container;
		CppServiceManager factory = container
				.getByClass(CppServiceManager.class);

		if (providerName == null)
			provider = factory.getServiceProvider(tClass, this, pattern);
		else
			provider = factory.getServiceProvider(tClass, this, pattern);

	}

	public synchronized void start(ServiceManager serviceManager)
			throws Exception {
		this.serviceManager = serviceManager;

		assert thread == null && state == ExecState.STOPPED;

		state = ExecState.STARTING;

		if (needThread) {

			thread = new Thread(this, consumerName);
			thread.start();

			while (state == ExecState.STARTING) {
				wait(1000);

				// for the case the thread crashed before
				// it called stopped()
				if (!thread.isAlive())
					break; // need to stop?
			}

		} else {
			state = ExecState.RUNNING;
		}
	}

	public synchronized void stop(Exception exception) {
		if (thread == null || !thread.isAlive()) {
			state = ExecState.STOPPED;
			return;
		}

		if (state != ExecState.RUNNING)
			return;

		state = ExecState.STOPPING;
		notify();

		while (state != ExecState.STOPPED)
			try {
				wait(1000);

				// for the case the thread crashed before
				// it called stopped()
				if (!thread.isAlive())
					break;
			} catch (InterruptedException e) {
				return;
			}

		assert !thread.isAlive();

		state = ExecState.STOPPED;
		thread = null;
	}

	/**
	 * Queries whether any associated threads should be terminated due to
	 * termination of the service
	 * 
	 * @return true iff the service is stopping
	 */
	protected boolean mustStop() {
		return state == ExecState.STOPPING;
	}

	/**
	 * Should be called from the associated thread when startup process is
	 * finnished
	 * 
	 */
	protected synchronized void started() {
		state = ExecState.RUNNING;
		notify();
	}

	/**
	 * Should be called from the associated thread when termination process is
	 * finnished
	 * 
	 */
	protected synchronized void stopped() {
		state = ExecState.STOPPED;
		notify();
	}

	/**
	 * Waits until termination of service is required
	 * 
	 */
	protected synchronized void waitForStop() {
		while (!mustStop())
			try {
				wait();
			} catch (InterruptedException e) {
			}
	}

	/**
	 * Implementation of this method must:
	 * <ul>
	 * <li>Call <tt>started</tt> when completely started (ready to provide a
	 * service).
	 * 
	 * <li>Terminate when <tt>mustStop</tt> returns true. Can use
	 * <tt>wait</tt> on this object to wait for this condition.
	 * 
	 * <li>Call <tt>stopped</tt> when completely stopped (before return),
	 * even when stopping abnormally
	 * </ul>
	 */
	public void run() {
	}

	@Override
	public String toString() {
		return consumerName;
	}

	protected State subscribe(Entity watcher, Key target, String subscriptID,
			int duration) {
		return provider.subscribe(watcher, target, subscriptID, duration);
	}
}
